package cn.tml.innermost.framework.utils;

import cn.tml.innermost.framework.cache.CachePrefix;
import cn.tml.innermost.framework.cache.impl.RedisCache;
import cn.tml.innermost.framework.entity.enums.ResultCode;
import cn.tml.innermost.framework.exception.ServiceException;
import cn.tml.innermost.framework.properties.EmailProperties;
import cn.tml.innermost.framework.security.context.UserContext;
import cn.tml.innermost.framework.security.enums.VerificationEnums;
import lombok.extern.slf4j.Slf4j;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;

import javax.mail.*;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Objects;
import java.util.Properties;
import java.util.Random;
import java.util.concurrent.TimeUnit;


/**
 * 只有在客户端获取验证码的时候，客户段需要带上VerificationEnums，
 * 服务器核对验证码的时候，客户端不需要带上VerificationEnums，VerificationEnums由controller层对应的接口提供，
 * 客户端只需要带上验证码和其他信息。
 */
@Component
@Slf4j
public class EmailUtil {

    private final RedisCache cache;

    private final Session session;
    // 发件人电子邮箱
    private final Properties properties = new Properties();

    private final ThreadPoolTaskExecutor executor;

    /**
     * 调用邮箱依赖，发送邮箱验证码
     *
     * @param email   目标邮箱
     * @param title   标题
     * @param code    邮箱验证码
     * @param context 邮箱内容描述
     */
    private void sendEmailCode(String email, String title, String code, String context) {
        File file = new File(Objects.requireNonNull(EmailUtil.class.getClassLoader().getResource("email-template.html")).getPath());
        InputStream stream = EmailUtil.class.getClassLoader().getResourceAsStream("email-template.html");
        try {
            final Document document = Jsoup.parse(stream, "utf-8", file.getAbsolutePath());
            if (document == null) {
                System.out.println("html文件丢失");
                return;
            }
            final Element codeElement = document.getElementById("code");
            final Element contextElement = document.getElementById("context");

            if (codeElement == null || contextElement == null) {
                System.out.println("html文件损坏");
                return;
            }
//            填写验证码
            codeElement.text(code);
//            填写邮箱内容
            contextElement.text(context);


            // 创建默认的 MimeMessage 对象
            MimeMessage message = new MimeMessage(session);
            // Set From: 设置发信人
            message.setFrom(new InternetAddress(properties.getProperty("account"), "验证码", "UTF-8"));
            // Set Subject: 设置标题
            message.setSubject(title, "UTF-8");
            //设置邮件内容
            message.setContent(document.toString(), "text/html;charset=utf-8");
            //设置收信人
            message.addRecipient(Message.RecipientType.TO, new InternetAddress(email));
            // 发送消息
            Transport.send(message);
        } catch (MessagingException | IOException e) {
            e.printStackTrace();
        }
    }

    public EmailUtil(RedisCache cache, EmailProperties emailProperties, ThreadPoolTaskExecutor executor) {
        this.cache = cache;
        this.executor = executor;
        // 加载配置
        properties.setProperty("mail.user", emailProperties.getUser());
        properties.setProperty("mail.smtp.host", emailProperties.getSmtpHost());
        properties.setProperty("mail.smtp.port", emailProperties.getPort());
        properties.setProperty("mail.transport.protocol", emailProperties.getProtocol());
        properties.setProperty("mail.smtp.auth", emailProperties.getSmtpAuth());
        properties.setProperty("account", emailProperties.getAccount());
        properties.setProperty("psw", emailProperties.getPsw());
        session = Session.getInstance(properties, new Authenticator() {
            @Override
            protected PasswordAuthentication getPasswordAuthentication() {
                return new PasswordAuthentication(properties.getProperty("account"), properties.getProperty("psw"));
            }
        });
    }


    /**
     * 对service层暴露的发送邮箱验证码方法
     *
     * @param email             目标邮箱
     * @param title             标题
     * @param verificationEnums 此次邮箱的验证类型
     */
    public void sendCode(String email, String title, VerificationEnums verificationEnums) {

        String uuid = UserContext.getCurrentUserUUID();
        if  (uuid == null) {
            throw new ServiceException(ResultCode.UUID_NOT_FIND);
        }


        int encryption = (Math.abs((new Random()).nextInt() % (int) 1e5));

        String [] patch={"00000","0000","000","00","0",""};
        int hash = email.hashCode();

        long result = hash ^ encryption;
        long codeLong =result % 100000;
        codeLong = codeLong>0?codeLong:-codeLong;
        String codeStr= codeLong+"";
        int len = codeStr.length();
        String code = patch[len]+codeStr;

        String context = String.valueOf(verificationEnums);
//        发送验证码
        this.executor.execute(() -> sendEmailCode(email, title, code, context));

        //缓存中写入要验证的信息
        cache.put(cacheKey(verificationEnums, uuid, email), code, 5L, TimeUnit.MINUTES);
    }


    /**
     * 对service层暴露的校验验证码的方法
     *
     * @param email             邮箱
     * @param verificationEnums 验证码类型
     * @param code              验证码
     * @return 校验结果
     */
    public boolean verifyCode(String email, VerificationEnums verificationEnums, String code) {
        String uuid = UserContext.getCurrentUserUUID();
        if (uuid == null) {
            throw new ServiceException(ResultCode.UUID_NOT_FIND);
        }
        Object val = cache.get(cacheKey(verificationEnums, uuid, email));
        if (code.equals(val)) {
            //校验之后，删除
            cache.remove(cacheKey(verificationEnums, uuid, email));
            return true;
        } else {
            return false;
        }

    }

    /**
     * 生成缓存key，邮箱验证码的缓存key都统一使用该方法生成
     *
     * @param verificationEnums 验证码的使用场景，如用来找回密码，用来登录等等
     * @param uuid              客户端的uuid
     * @param email             邮箱
     * @return 生成的缓存key
     */
    private static String cacheKey(VerificationEnums verificationEnums, String uuid, String email) {
        return CachePrefix.EMAIL_CODE.getPrefix() + verificationEnums.name() + uuid + email;
    }
}
